2019_bach_fugues.py

#

SPDX-FileCopyrightText: 2019 Julia Barnoin & Guillaume Charinet SPDX-FileCopyrightText: 2024 AlICe laboratory https://alicelab.be

SPDX-License-Identifier: GPL-3.0-or-later

#

The Art of the Fugue # Authors: [Julia & Guillaume] # Date: [28.11.2019] # Blender version: [2.8] # OS: [Windows 10] #

from random import random, choice
from mathutils import Vector, Matrix
from mathutils.noise import random, seed_set
import bpy, bmesh


bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.delete(use_global=False, confirm=False)

context = bpy.context
scene = context.scene
#

Making new materials:

Red = bpy.data.materials.new("Red")
Red.diffuse_color = (0.8, 0, 0.000869932, 1)

Blue = bpy.data.materials.new("Blue")
Blue.diffuse_color = (0.113655, 0.510074, 0.8, 1)

Green = bpy.data.materials.new("Green")
Green.diffuse_color = (0.165985, 0.8, 0.0555711, 1)
#

will apply the choosen material to the generated geometry

def apply_mat(mat):
#
    obj = bpy.context.active_object
    obj.data.materials.append(mat)
#

use of the golden ratio as proportion ratio for scaling and placing the geometry

def fib(a):
#
    fibonaci = []
    x = 0
    y = 1
    while x < a:
#

print(x)

        fibonaci.append(x)
        x, y = y, x + y
#

print (fibonaci)

    return fibonaci[4:]
#

will create a rectangle with locations and dimensions data

def boite(Lx, Ly, Lz, Sx, Sy, Sz):
#
    bpy.ops.mesh.primitive_cube_add(size=1, location=(Lx, Ly, Lz))
    bpy.context.object.dimensions = (Sx, Sy, Sz)
    return bpy.context.selected_objects[0]
#
def get_BoundBox(ob):
#

returns the corners of the bounding box of an object in world coordinates

    from mathutils import Vector

    bpy.context.view_layer.update()
    bbox_corners = [ob.matrix_world @ Vector(corner) for corner in ob.bound_box]

    return bbox_corners
#
def check_Collision(box1, box2):
#

Check Collision of 2 Bounding Boxes box1 & box2 must be lists of Vectors, containg the edges of the bounding boxes

print(‘COLLISION CHECK’) print(‘\n[START] Check collision with:’)

    x_max = max([e[0] for e in box1])
    x_min = min([e[0] for e in box1])
    y_max = max([e[1] for e in box1])
    y_min = min([e[1] for e in box1])
    z_max = max([e[2] for e in box1])
    z_min = min([e[2] for e in box1])
#

print(‘[INFO] Box1 min %.2f, %.2f, %.2f’ % (x_min, y_min, z_min)) print(‘[INFO] Box1 max %.2f, %.2f, %.2f’ % (x_max, y_max, z_max))

    x_max2 = max([e[0] for e in box2])
    x_min2 = min([e[0] for e in box2])
    y_max2 = max([e[1] for e in box2])
    y_min2 = min([e[1] for e in box2])
    z_max2 = max([e[2] for e in box2])
    z_min2 = min([e[2] for e in box2])
#

print(‘[INFO] Box2 min %.2f, %.2f, %.2f’ % (x_min2, y_min2, z_min2)) print(‘[INFO] Box2 max %.2f, %.2f, %.2f’ % (x_max2, y_max2, z_max2))

#

Check max and min values of coordinates Here, values must be inferior or equal

    isColliding = (
        (
            (x_max >= x_min2 and x_max <= x_max2)
            or (x_min <= x_max2 and x_min >= x_min2)
            or (x_max2 <= x_max and x_min2 >= x_min)
            or (x_max <= x_max2 and x_min >= x_min2)
        )
        and (
            (y_max >= y_min2 and y_max <= y_max2)
            or (y_min <= y_max2 and y_min >= y_min2)
            or (y_max2 <= y_max and y_min2 >= y_min)
            or (y_max <= y_max2 and y_min >= y_min2)
        )
        and (
            (z_max >= z_min2 and z_max <= z_max2)
            or (z_min <= z_max2 and z_min >= z_min2)
            or (z_max2 <= z_max and z_min2 >= z_min)
            or (z_max <= z_max2 and z_min >= z_min2)
        )
    )
#

if isColliding: print(‘[RESULT] Collision found!’, box1.name, ‘and’, box2.name) else: print(‘[RESULT] No collision found.’)

#

print(‘[END]\n’)

    return isColliding
#

will return collision check as True

def testCollision(a, b):
#
    collision = check_Collision(get_BoundBox(a), get_BoundBox(b))
    if collision:
        print("[RESULT] Collision found!", a.name, "and", b.name)
    return collision
#

will delete geometry

def deleteBoite():
#
    bpy.ops.object.delete(use_global=False, confirm=False)
#

will list all geometry that already exist

def list_all():
#
    res = []
    existing_objects = list(bpy.data.objects)
    for obj in existing_objects:
        res.append(obj)
    return res
#

subject generation on the X axis

def make_subject_1():
#
    fibonaci = fib(100)  #
    fibonaciX = fib(
        20
    )  #  Use of the fibonacci function for the localisation and dimmension data
#
    Lx = 0
    res = []

    while Lx <= 100:  # Start of the main loop

        Ly = choice(fibonaci)  #
        Lz = choice(fibonaci)  #
        Sx = choice(
            fibonaciX
        )  # -------->  pick a random number in the fibonacci sequence
        Sy = choice(fibonaci)  #
        Sz = choice(fibonaci)  #

        if (
            Lx == 0 or Lx > 50 and Lx < 60 or Lx > 70 and Lx < 80
        ):  # condition to generate attachement points

            if (
                Ly + Sy < 89 and Lz + Sz < 89
            ):  # condition to restrain geometry to our 100x100 cube

                Sx = 21  # ---------> restrainng size of our attachement points
                Sy >= 21  #
                newboite = boite(Lx + (Sx / 2), Ly, Lz, Sx, Sy, Sz)
                apply_mat(Red)

                if not Lx == 0 and not testCollision(
                    oldboite, newboite
                ):  # checking collision so all are lincked by one of their faces

                    deleteBoite()

                else:
                    oldboite = newboite
                    res.append(oldboite)

                    Lx = Lx + Sx  # count will advance as the next geometry localisation
                    print(Lx)

        else:

            if (
                Ly + Sy < 89 and Lz + Sz < 89
            ):  # condition to generate in between attachement points

                Sx < 13  # ---------> restrainng size of our "notes"
                newboite = boite(Lx + (Sx / 2), Ly, Lz, Sx, Sy, Sz)
                apply_mat(Red)

                if not Lx == 0 and not testCollision(
                    oldboite, newboite
                ):  # checking collision so all are lincked by one of their faces

                    deleteBoite()

                else:
                    oldboite = newboite
                    res.append(oldboite)

                    Lx = Lx + Sx  # count will advance as the next geometry localisation
                    print(Lx)

    return res
#
#
def make_inversion(
    list, Axis, Sx, Sy, Sz
):  # making an inversion of the previous generated subject so they can be intricated

    res = []

    bpy.ops.object.select_all(action="DESELECT")
    for obj in list:
        obj.select_set(state=True)

    bpy.ops.object.duplicate_move()

    trans = bpy.ops.transform.rotate
    trans(value=3.14159, orient_axis=Axis)
    bpy.ops.transform.resize(
        value=(Sx, Sy, Sz)
    )  # making a mirror version of our subject
    for obj in bpy.context.selected_objects:
        res.append(obj)

    return res
#
def place_inversion(
    list, inversion, x, y, z
):  # placing our inversion so it cannot collide with the existent geometry

    bpy.ops.object.select_all(action="DESELECT")

    count = 0
    collision = True

    print(list)

    while collision:  # Starting of the loop
        count += 1
        print("loop number", count)
        collision = False

        for C1 in list:  #
            for (
                C2
            ) in (
                inversion
            ):  # ---------------> comparing existent geometry to the inversion

                print("test:", C1.name, C2.name)

                while testCollision(C1, C2):  # checking collision
                    bpy.ops.object.select_all(action="DESELECT")
                    for obj in inversion:
                        obj.select_set(state=True)

                    bpy.ops.transform.translate(
                        value=(x, y, z)
                    )  # if collision append the whole inversion will move again and again
                    collision = True

                print(collision)

                if collision:
                    break

            if collision:
                break

    print("number of test loops", count, collision)
#
def make_voice4(list):  # generates voice 4 as subject 1 inversion

    inversion = make_inversion(list, "Z", 1, 1, -1)
    bpy.ops.transform.translate(
        value=(0, 50, 0),
        orient_type="GLOBAL",
        orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
        orient_matrix_type="GLOBAL",
        constraint_axis=(True, False, False),
        mirror=True,
        use_proportional_edit=False,
        proportional_edit_falloff="SMOOTH",
        proportional_size=1,
        use_proportional_connected=False,
        use_proportional_projected=False,
    )
    place_inversion(list, inversion, 0, 0.5, 0.5)
#
def make_subject_2():  # subject generation on the Y axis

    existing_objects = list(bpy.data.objects)

    fibonaci = fib(100)  #
    fibonaciX = fib(
        20
    )  #  Use of the fibonacci function for the localisation and dimmension data
#
    Ly = 0
    res = []

    while Ly <= 100:  # Start of the main loop

        Lx = choice(fibonaci)  #
        Lz = choice(fibonaci)  #
        Sx = choice(
            fibonaci
        )  # -------->  pick a random number in the fibonacci sequence
        Sy = choice(fibonaciX)  #
        Sz = choice(fibonaci)  #

        if (
            Ly == 0 or Ly > 50 and Ly < 60 or Ly > 70 and Ly < 80
        ):  # condition to generate attachement points

            if (
                Lx + Sx < 89 and Lz + Sz < 89
            ):  # condition to restrain geometry to our 100x100 cube

                Sy = 21  # ---------> restrainng size of our attachement points

                newboite = boite(Lx, Ly + (Sy / 2), Lz, Sx, Sy, Sz)
                apply_mat(Blue)

                if Ly == 0:  # exeption for the starting attachement point
                    print("LOOP 1st CUBE")
                    collision = True

                    while (
                        collision
                    ):  # loop checking collision so new subject will not collide with other existing geometry
                        collision = False

                        for obj in existing_objects:

                            print("test:", newboite, obj)

                            if testCollision(newboite, obj):
                                collision = True
                                deleteBoite()
                                break
                            print(collision)

                        if collision:
                            break

                    if not collision:
                        Ly = (
                            Ly + Sy
                        )  # count will advance as the next geometry localisation
                        oldboite = newboite
                        res.append(oldboite)
                        print(Ly)

                else:  # generating our others attachement points
                    print("LOOP 2")
                    if not testCollision(
                        oldboite, newboite
                    ):  # checking collision so all are lincked by one of their faces
                        deleteBoite()

                    else:
                        collision = True

                        while (
                            collision
                        ):  # loop checking collision so new subject will not collide with other existing geometry
                            collision = False

                            for obj in existing_objects:

                                print("test:", newboite, obj)

                                if testCollision(newboite, obj):
                                    collision = True
                                    deleteBoite()
                                    break
                                print(collision)

                            if collision:
                                break

                        if not collision:
                            Ly = (
                                Ly + Sy
                            )  # count will advance as the next geometry localisation
                            oldboite = newboite
                            res.append(oldboite)
                            print(Ly)

        else:
            print("LOOP 3")
            if (
                Lx + Sx < 89 and Lz + Sz < 89
            ):  # condition to generate in between attachement points

                newboite = boite(Lx, Ly + (Sy / 2), Lz, Sx, Sy, Sz)
                apply_mat(Blue)

                if not testCollision(
                    oldboite, newboite
                ):  # checking collision so all are lincked by one of their faces
                    deleteBoite()

                else:
                    collision = True

                    while (
                        collision
                    ):  # loop checking collision so new subject will not collide with other existing geometry
                        collision = False

                        for obj in existing_objects:

                            print("test:", newboite, obj)

                            if testCollision(newboite, obj):
                                collision = True
                                deleteBoite()
                                break
                            print(collision)

                        if collision:
                            break

                    if not collision:
                        Ly = (
                            Ly + Sy
                        )  # count will advance as the next geometry localisation
                        oldboite = newboite
                        res.append(oldboite)
                        print(Ly)
#

break

    return res
#
def make_voice3(list):  # generates voice 3 as subject 2 inversion
    voices_2_4_1 = list_all()
    inversion_2 = make_inversion(list, "Z", 1, 1, -1)
    bpy.ops.transform.translate(
        value=(75, 0, 0),
        orient_type="GLOBAL",
        orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
        orient_matrix_type="GLOBAL",
        constraint_axis=(True, False, False),
        mirror=True,
        use_proportional_edit=False,
        proportional_edit_falloff="SMOOTH",
        proportional_size=1,
        use_proportional_connected=False,
        use_proportional_projected=False,
    )
#

resizing as a augmentation in music theory

    bpy.ops.transform.resize(
        value=(0.80, 0.80, 0.80),
        orient_type="GLOBAL",
        orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
        orient_matrix_type="GLOBAL",
        mirror=True,
        use_proportional_edit=False,
        proportional_edit_falloff="SMOOTH",
        proportional_size=1,
        use_proportional_connected=False,
        use_proportional_projected=False,
    )
    place_inversion(voices_2_4_1, inversion_2, 0.1, -0.1, 0.3)
#
def make_subject_3():  # subject generation on the X axis

    existing_objects = list(bpy.data.objects)

    fibonaci = fib(100)  #
    fibonaciX = fib(
        20
    )  #  Use of the fibonacci function for the localisation and dimmension data
#
    Lx = 0
    res = []

    while Lx <= 100:  # Start of the main loop

        Ly = choice(fibonaci)  #
        Lz = choice(fibonaci)  #
        Sx = choice(
            fibonaciX
        )  # -------->  pick a random number in the fibonacci sequence
        Sy = choice(fibonaci)  #
        Sz = choice(fibonaci)  #

        if (
            Lx == 0 or Lx > 50 and Lx < 60 or Lx > 70 and Lx < 80
        ):  # condition to generate attachement points

            if (
                Ly + Sy < 89 and Lz + Sz < 89
            ):  # condition to restrain geometry to our 100x100 cube

                Sx = 21  #
                Sz >= 21  # ---------> restrainng size of our attachement points
                newboite = boite(Lx + (Sx / 2), Ly, Lz, Sx, Sy, Sz)
                apply_mat(Green)

                if Lx == 0:  # exeption for the starting attachement point
                    print("LOOP 1st CUBE")
                    collision = True

                    while (
                        collision
                    ):  # loop checking collision so new subject will not collide with other existing geometry
                        collision = False

                        for obj in existing_objects:

                            print("test:", newboite, obj)

                            if testCollision(newboite, obj):
                                collision = True
                                deleteBoite()
                                break
                            print(collision)

                        if collision:
                            break

                    if not collision:
                        Lx = (
                            Lx + Sx
                        )  # count will advance as the next geometry localisation
                        oldboite = newboite
                        res.append(oldboite)
                        print(Lx)
#

break

                else:  # generating our others attachement points
                    print("LOOP 2")
                    if not testCollision(
                        oldboite, newboite
                    ):  # checking collision so all are lincked by one of their faces
                        deleteBoite()

                    else:  # loop checking collision so new subject will not collide with other existing geometry
                        collision = True

                        while collision:
                            collision = False

                            for obj in existing_objects:

                                print("test:", newboite, obj)

                                if testCollision(newboite, obj):
                                    collision = True
                                    deleteBoite()
                                    break
                                print(collision)

                            if collision:
                                break

                        if not collision:
                            Lx = (
                                Lx + Sx
                            )  # count will advance as the next geometry localisation
                            oldboite = newboite
                            res.append(oldboite)
                            print(Lx)

        else:
            print("LOOP 3")
            if (
                Ly + Sy < 89 and Lz + Sz < 89
            ):  # condition to generate in between attachement points

                newboite = boite(Lx + (Sx / 2), Ly, Lz, Sx, Sy, Sz)
                apply_mat(Green)

                if not testCollision(
                    oldboite, newboite
                ):  # checking collision so all are lincked by one of their faces
                    deleteBoite()

                else:  # loop checking collision so new subject will not collide with other existing geometry
                    collision = True

                    while collision:
                        collision = False

                        for obj in existing_objects:

                            print("test:", newboite, obj)

                            if testCollision(newboite, obj):
                                collision = True
                                deleteBoite()
                                break
                            print(collision)

                        if collision:
                            break

                    if not collision:
                        Lx = (
                            Lx + Sx
                        )  # count will advance as the next geometry localisation
                        oldboite = newboite
                        res.append(oldboite)
                        print(Lx)

    return res
#
def make_voice6(list):  # generates voice 6 as subject 3 inversion

    voices_2_4_1 = list_all()
    inversion_2 = make_inversion(list, "Z", 1, 1, -1)
    bpy.ops.transform.translate(
        value=(0, 75, 0),
        orient_type="GLOBAL",
        orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
        orient_matrix_type="GLOBAL",
        constraint_axis=(True, False, False),
        mirror=True,
        use_proportional_edit=False,
        proportional_edit_falloff="SMOOTH",
        proportional_size=1,
        use_proportional_connected=False,
        use_proportional_projected=False,
    )
    place_inversion(voices_2_4_1, inversion_2, 0.1, -0.1, 0.3)
#
#

Calling all our subject and response

subject1 = make_subject_1()
make_voice4(subject1)
subject2 = make_subject_2()
make_voice3(subject2)
subject3 = make_subject_3()
make_voice6(subject3)